package test;

import java.lang.Math;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import model.CrossOverInput;
import model.Customer;
import model.Node;
import model.Warehouse;

public class algorithm {
	
	static int capacityw1 = 22;
	static int capacityw2 = 23;
	static int capacityw3 = 23;
	static List<Node> nodeList;
	
	public static void main(String args[]){
		home();
	}
	
	public static void home(){
		
		double[] fitnessArray = new double[40];
		boolean demandPossible = false;
		int totalDemandOnWarehouse = 0;
		
		nodeList = new ArrayList<Node>(18);
		nodeList.add(0,null);
		nodeList.add(1,null);
		nodeList.add(2,null);
		nodeList.add(3,new Customer(539,850,4));
		nodeList.add(4,new Customer(519,661,3));
		nodeList.add(5,new Customer(599,715,2));
		nodeList.add(6,new Customer(538,431,7));
		nodeList.add(7,new Customer(342,772,7));
		nodeList.add(8,new Customer(430,845,3));
		nodeList.add(9,new Customer(499,463,2));
		nodeList.add(10,new Customer(462,652,1));
		nodeList.add(11,new Customer(388,984,6));
		nodeList.add(12,new Customer(409,613,9));
		nodeList.add(13,new Customer(772,796,10));
		nodeList.add(14,new Customer(435,314,3));
		nodeList.add(15,new Customer(350,742,1));
		nodeList.add(16,new Customer(483,663,4));
		nodeList.add(17,new Customer(894,799,3));
		
		int[][] generation = new int[40][21];
		List<Integer> possibleCustomers = new ArrayList<Integer>(Arrays.asList(3,4,5,6,7,8,9,10,11,12,13,14,15,16,17)); 
		
		for(int i=0;i<40;i++){
			for(int j=0;j<6;j++){
				generation[i][j]=randInt(300,1000);
			}
			
			do{
				demandPossible = true;
				//System.out.println("Shuffling");
				Collections.shuffle(possibleCustomers);
				
				demandPossible = checkFeasibility(possibleCustomers);
				
			}while(demandPossible != true);
			
			//System.out.println("FOUND ONE LINE"+totalDemandOnWarehouse);
			
			for(int j=6;j<21;++j){
				generation[i][j]=possibleCustomers.get(j-6);
			}
		}
		
		
		List<CrossOverInput> crossOverInput = new ArrayList<>();
		for(int i=0;i<40;i++){
			fitnessArray[i] = process(generation[i]);
			crossOverInput.add(i,new CrossOverInput(generation[i], fitnessArray[i]));
		}
		
		long z = 1;
		double oldtemp = 0;
		double temp = 0;
		while(true){
			Collections.sort(crossOverInput);
			z++;
			oldtemp = temp;
			temp = crossOverInput.get(0).fitnessValue;
			
			if(temp < oldtemp){
				System.out.println("\nBest fitness value found as : "+ crossOverInput.get(0).fitnessValue +" of generation "+ z);
				System.out.print(Arrays.toString(crossOverInput.get(0).chromosome));
				System.out.println("\n\n");
			}
			
			System.out.print(".");
			
			doModification(crossOverInput);
			
			//System.out.println("Ending generation job\n\n");
		}
		
	}

	public static void doModification(List<CrossOverInput> crossOverInput){
		
		//Mutation 10-20
		//System.out.println("Mutation random\n");
		
		for(int i=10;i<40;++i){
			double randZeroToOne = Math.random();
			if(randZeroToOne < 0.1){
				//System.out.println("Mutated index "+i+" of crossOverInput");
				crossOverInput.set(i, mutate(crossOverInput.get(i))); 
			}
		}
		
	}

	private static CrossOverInput mutate(CrossOverInput crossOverInput) {
		
		boolean demandPossible = true;
		int[] tempAssignBack;
		
		do{
			List<Integer> chromosome = new ArrayList<>();
			for(int i=0;i<crossOverInput.chromosome.length;++i){
				chromosome.add(new Integer(crossOverInput.chromosome[i]));
			}
			
			/* START Mutate Warehouse Coordinate 0 to 5 of crossOverInput.chromosome array*/
			
			int randNum = randInt(0, 5);
			
			if(randNum % 2 == 0){
				chromosome.set(randNum,randInt(300,1000));
				chromosome.set(randNum+1,randInt(300,1000));
			}
			else{
				chromosome.set(randNum-1,randInt(300,1000));
				chromosome.set(randNum,randInt(300,1000));
			}
			
			/* END Mutate Warehouse Coordinate index 0 to 5 of crossOverInput.chromosome array*/
			
			/* START Mutate customer index 6 to 20 of crossOverInput.chromosome array */
			int pos1,pos2,pos3;
			
			do{
				pos1 = randInt(6,20);
			}while(!(pos1 <= 18));
			
			do{
				pos2 = randInt(6,20);
			}while(pos2 == pos1 && pos2 < 18);
	
			do{
				 pos3 = randInt(6,20);
			}while(pos3 == pos1 && pos3 == pos2 && pos3 < 18);
	
			
			List<Integer> posList = new ArrayList<Integer>();
			posList.add(pos1);
			posList.add(pos2);
			posList.add(pos3);
			
			Collections.sort(posList);
			
			//System.out.println("3 random number generated in mutation are " + posList.get(0)+" "+posList.get(1)+" "+posList.get(2));
			
			Collections.swap(chromosome,posList.get(0)+1,posList.get(1));
			Collections.swap(chromosome,posList.get(0)+2,posList.get(2));
			
			tempAssignBack = new int[chromosome.size()];
			for(int i=0;i<chromosome.size();++i){
				tempAssignBack[i] = chromosome.get(i);
			}
			
			// Check feasibility of mutated chromosome
			List<Integer> feasibilityCheckList = new ArrayList<>();
			for(int i=6;i<21;i++){
				feasibilityCheckList.add(tempAssignBack[i]);
			}
			
			demandPossible = checkFeasibility(feasibilityCheckList); 
			
			if(demandPossible == false){
				//System.out.println("Mutation not feasible RETRYING");
			}
			
		}while(demandPossible != true);
		
		if(demandPossible == true){
			double fitnessValueOfMutation = process(tempAssignBack);
			crossOverInput.chromosome = tempAssignBack;
			crossOverInput.fitnessValue = fitnessValueOfMutation;
		}
		
		//System.out.println("Mutation DONE. Retaining it.\n");
	
		return crossOverInput;

		/* END Mutate customer index 6 to 20 of crossOverInput.chromosome array */
		
	}

	public static double process(int[] generation){
		
		//System.out.println("Invoked with ");
		for(int i=0;i<21;++i){
			//System.out.print(generation[i]+" , ");
		}
		//System.out.println("\n\n");
		
		//nodeList.add(0,new Warehouse(727,915));
		//nodeList.add(1,new Warehouse(418,900));
		//nodeList.add(2,new Warehouse(816,820));
		nodeList.add(0,new Warehouse(generation[0],generation[1],capacityw1));
		nodeList.add(1,new Warehouse(generation[2],generation[3],capacityw2));
		nodeList.add(2,new Warehouse(generation[4],generation[5],capacityw3));
		nodeList.add(3,new Customer(539,850,4));
		nodeList.add(4,new Customer(519,661,3));
		nodeList.add(5,new Customer(599,715,2));
		nodeList.add(6,new Customer(538,431,7));
		nodeList.add(7,new Customer(342,772,7));
		nodeList.add(8,new Customer(430,845,3));
		nodeList.add(9,new Customer(499,463,2));
		nodeList.add(10,new Customer(462,652,1));
		nodeList.add(11,new Customer(388,984,6));
		nodeList.add(12,new Customer(409,613,9));
		nodeList.add(13,new Customer(772,796,10));
		nodeList.add(14,new Customer(435,314,3));
		nodeList.add(15,new Customer(350,742,1));
		nodeList.add(16,new Customer(483,663,4));
		nodeList.add(17,new Customer(894,799,3));
		
		int[][] distMatrix = new int[18][18];
		for(int i=0;i<18;i++){
			for(int j=0;j<18;j++){
				distMatrix[i][j] = -99;
			}
		}
		
		for(int i=0;i<18;i++){
			for(int j=0;j<18;j++){
				int distance = (int) nodeList.get(i).distanceBetween(nodeList.get(j));
				distMatrix[i][j]=distance;
			}
		}
		
		//printBoard(distMatrix);
		
		int[][] x1 = new int[18][18];
		int[][] x2 = new int[18][18];
		int[][] x3 = new int[18][18];
		
		int[] a = new int[15];
		for(int i=6; i< 21;i++){
			a[i-6] = generation[i];
			//System.out.println("a["+(i-6)+"]="+a[i-6]);
		}
		
		
		
		//a = {3,11,12,6,7,9,0,14,13,4,2,1,10,8)
		
		
		x1[0][a[0]] = 1;
		x1[a[0]][a[1]] = 1;
		x1[a[1]][a[2]] = 1;
		x1[a[2]][a[3]] = 1;
		x1[a[3]][a[4]] = 1;
		x1[a[4]][0] = 1;
		
		x2[1][a[5]] = 1;
		x2[a[5]][a[6]] = 1;
		x2[a[6]][a[7]] = 1;
		x2[a[7]][a[8]] = 1;
		x2[a[8]][a[9]] = 1;
		x2[a[9]][1] = 1;
		
		x3[2][a[10]] = 1;
		x3[a[10]][a[11]] = 1;
		x3[a[11]][a[12]] = 1;
		x3[a[12]][a[13]] = 1;
		x3[a[13]][a[14]] = 1;
		x3[a[14]][2] = 1;
		
		int c1 = 0,c2 = 0,c3 = 0;
		double e1,e2,e3;
		double resp1 = 0,resp2 = 0, resp3 = 0, totalresp = 0;
		
		for(int i=0;i<5;i++){
			c1 += ((Customer)nodeList.get(a[i])).demand; 
		}
		
		for(int i=5;i<10;i++){
			c2 += ((Customer)nodeList.get(a[i])).demand; 
		}
		
		
		for(int i=10;i<15;i++){
			c3 += ((Customer)nodeList.get(a[i])).demand; 
		}
		
		for(int i=0;i<18;i++){
			for(int j=0;j<18;++j){
				if(x1[i][j] == 1){
					if(c1 >= 0 && c1 <=5){
						e1 = 0.773;
					}
					else if(c1 > 5 && c1 <=10){
						e1 = 0.831;
					}
					else if(c1 > 10 && c1 <= 15){
						e1=0.90;
					}
					else if(c1>15 && c1<=20){
						e1 = 0.958;
					}
					else{
						e1 = 1.018;
					}
					
					//System.out.println("Entering i="+i+",j="+j);
					//System.out.println("c1 value selected = "+c1);
					//System.out.println("e1 value selected = "+e1);
					//System.out.println("dij value selected = "+distMatrix[i][j]);
					resp1 = resp1 + (distMatrix[i][j] * x1[i][j] * e1);
					//System.out.println("Curretn resp1 "+resp1);
					if(nodeList.get(j) instanceof Customer){
						c1 = c1 - ((Customer)nodeList.get(j)).demand;
						//System.out.println("Dropping load of node index  "+ j +"="+ ((Customer)nodeList.get(j)).demand);
					}
					//System.out.println("New c1 value is "+c1);
					//System.out.println("\n");
				}
				
				if(x2[i][j] == 1){
					if(c2 >= 0 && c2 <=5){
						e2 = 0.773;
					}
					else if(c2 > 5 && c2 <=10){
						e2 = 0.831;
					}
					else if(c2 > 10 && c2 <= 15){
						e2=0.90;
					}
					else if(c2>15 && c2<=20){
						e2 = 0.958;
					}
					else{
						e2 = 1.018;
					}
					
					//System.out.println("Entering i="+i+",j="+j);
					//System.out.println("c2 value selected = "+c2);
					//System.out.println("e2 value selected = "+e2);
					//System.out.println("dij value selected = "+distMatrix[i][j]);
					resp2 = resp2 + (distMatrix[i][j] * x2[i][j] * e2);
					//System.out.println("Curretn resp1 "+resp1);
					if(nodeList.get(j) instanceof Customer){
						c2 = c2 - ((Customer)nodeList.get(j)).demand;
						//System.out.println("Dropping load of node index  "+ j +"="+ ((Customer)nodeList.get(j)).demand);
					}
					//System.out.println("New c2 value is "+c2);
					//System.out.println("\n");
				}
				
				if(x3[i][j] == 1){
					if(c3 >= 0 && c3 <=5){
						e3 = 0.773;
					}
					else if(c3 > 5 && c3 <=10){
						e3 = 0.831;
					}
					else if(c3 > 10 && c3 <= 15){
						e3=0.90;
					}
					else if(c3>15 && c3<=20){
						e3 = 0.958;
					}
					else{
						e3 = 1.018;
					}
					
					//System.out.println("Entering i="+i+",j="+j);
					//System.out.println("c3 value selected = "+c3);
					//System.out.println("e4 value selected = "+e3);
					//System.out.println("dij value selected = "+distMatrix[i][j]);
					resp3 = resp3 + (distMatrix[i][j] * x3[i][j] * e3);
					//System.out.println("Curretn resp1 "+resp1);
					if(nodeList.get(j) instanceof Customer){
						c3 = c3 - ((Customer)nodeList.get(j)).demand;
						//System.out.println("Dropping load of node index  "+ j +"="+ ((Customer)nodeList.get(j)).demand);
					}
					//System.out.println("New c value is "+c3);
					//System.out.println("\n");
				}
			}
		}
		
		totalresp = resp1 + resp2 + resp3;
		//System.out.println("resp1 "+resp1);
		//System.out.println("resp2 "+resp2);
		//System.out.println("resp3 "+resp3);
		//System.out.println("TotalResp : "+totalresp);
		
		return totalresp;
	}
	
	private static boolean checkFeasibility(List<Integer> possibleCustomers) {
		
		int totalDemandOnWarehouse;
		totalDemandOnWarehouse = 0;
		boolean demandPossible = true;
		
		for(int k=0;k<5;++k){
			totalDemandOnWarehouse += ((Customer) nodeList.get(possibleCustomers.get(k))).demand;
		}
		
		if(totalDemandOnWarehouse >= capacityw1){
			demandPossible = false;
			//System.out.println("Cannot satisfy demand from warehouse 1 for "+totalDemandOnWarehouse);
			return demandPossible;
		}
		
		totalDemandOnWarehouse = 0;
		for(int k=5;k<10;++k){
			totalDemandOnWarehouse += ((Customer) nodeList.get(possibleCustomers.get(k))).demand;
		}
		
		if(totalDemandOnWarehouse >= capacityw2){
			demandPossible = false;
			//System.out.println("Cannot satisfy demand from warehouse 2 for "+totalDemandOnWarehouse);
			return demandPossible;
		}
		
		totalDemandOnWarehouse = 0;
		for(int k=10;k<15;++k){
			totalDemandOnWarehouse += ((Customer) nodeList.get(possibleCustomers.get(k))).demand;
		}
		
		if(totalDemandOnWarehouse >= capacityw3){
			demandPossible = false;
			//System.out.println("Cannot satisfy demand from warehouse 3 for "+totalDemandOnWarehouse);
			return demandPossible;
		}
		return demandPossible;
		
	}
	
	
	public static void printBoard(int[][] board){
		
		for(int i=0;i<board.length+1;++i){
			for(int j=0;j<board[0].length+1;++j){
			 if(i==0 && j==0){
				 System.out.print("\t");
			 }
			 else if(i==0){
				 System.out.print(j-1+"\t");
			 }
			 else if(j==0){
				 System.out.print(i-1+"\t");
			 }
			 else if(board[i-1][j-1] == -99){
				 System.out.print("--"+"\t");
			 }
			 else{
				 System.out.print(board[i-1][j-1]+"\t");
			 }
			}
			System.out.println("");
		}
	}
	
	public static int randInt(int min, int max) {

	    // Usually this can be a field rather than a method variable
	    Random rand = new Random();

	    // nextInt is normally exclusive of the top value,
	    // so add 1 to make it inclusive
	    int randomNum = rand.nextInt((max - min) + 1) + min;

	    return randomNum;
	}
}
